package io.gitee.zhucan123.core.infrastructure.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Stack;

/**
 * 用于解析缩进结构的文档
 * @author guodu
 * @date 2020/1/7
 */
public abstract class OutlineVisitor<T> {

  int tabstop(String str) {
    int c = 0;
    for (int i = 0; i < str.length(); i++) {
      char ch = str.charAt(i);
      if (ch == ' ' || ch == '\t') {
        c++;
      } else {
        break;
      }
    }
    return c;
  }

  protected abstract T goDown(T parent, String line);
  protected abstract T onBrother(T parent, String line);
  protected abstract T goUp(T parent, String line);

  public void visit(InputStream st) throws IOException {
    InputStreamReader reader = new InputStreamReader(st);
    BufferedReader breader = new BufferedReader(reader);
    try {
      Stack<String> tabstop = new Stack<String>();
      Stack<T> path = new Stack<T>();
      while (true) {
        String line = breader.readLine();
        if (line == null) {
          break;
        }
        if (line.length() == 0) {
          continue;
        }
        int currTabWidth = tabstop(line);
        String currTabStop = line.substring(0, currTabWidth);
        String parentStop = tabstop.size() > 0 ? tabstop.peek() : null;
        line = line.substring(currTabWidth);
        if (parentStop == null || currTabWidth > parentStop.length()) {
          // down
          T parent = path.size() > 0 ? path.peek() : null;
          T child = goDown(parent, line);
          path.add(child);
          tabstop.add(currTabStop);
        } else if (currTabWidth == parentStop.length()){
          // 同级
          if (path.size() > 0) {
            path.pop();
          }
          T brother = onBrother(path.size() > 0 ? path.peek() : null, line);
          path.add(brother);
        } else {
          // up
          String parentTabStop;
          while (tabstop.size() > 0) {
            parentTabStop = tabstop.pop();
            path.pop();
            if (parentTabStop.length() == currTabWidth) {
              break;
            }
          }
          T upNode = goUp(path.size() > 0 ? path.peek() : null, line);
          tabstop.add(currTabStop);
          path.add(upNode);
        }
      }
    } finally {
      breader.close();
      reader.close();
    }
  }

}
